#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
PowerTrace预ProcessCommand行Tool

用法:
    python preprocess_traces.py input.npz -o output.npz [options]

Example:
    # Basic预Process（Alignment、Filtering、Normalization）
    python preprocess_traces.py traces.npz -o traces_processed.npz
    
    # Complete预Process（IncludeDenoising、OutlierRemove）
    python preprocess_traces.py traces.npz -o traces_processed.npz --denoise --remove-outliers
    
    # 自定义Parameters
    python preprocess_traces.py traces.npz -o traces_processed.npz \
        --align-method dtw \
        --filter-cutoff 0.2 \
        --norm-method robust \
        --outlier-threshold 2.5
    
    # 降维Process
    python preprocess_traces.py traces.npz -o traces_pca.npz --pca --pca-components 100
    
    # Extract时间窗口
    python preprocess_traces.py traces.npz -o traces_window.npz --window 100:500
"""

import numpy as np
import argparse
import logging
import sys
import os
from typing import Optional
from trace_preprocessing import TracePreprocessor, PreprocessingConfig

# Configuration日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%H:%M:%S'
)
logger = logging.getLogger(__name__)


def load_traces_from_npz(input_file: str):
    """
    FromnpzFileLoadTraceData
    
    SupportFormat:
    1. 标准Format: 'cpu_traces' (来自collect_traces.py)
    2. 预ProcessFormat: 'traces' (来自trace_preprocessing.py)
    """
    if not os.path.exists(input_file):
        raise FileNotFoundError(f"FileNot存In: {input_file}")
    
    data = np.load(input_file)
    
    # TryNot同的键名
    if 'cpu_traces' in data:
        traces = data['cpu_traces']
        logger.info(f"[OK] Load {len(traces)} 条Trace (来自采集Data)")
    elif 'traces' in data:
        traces = data['traces']
        logger.info(f"[OK] Load {len(traces)} 条Trace (来自预ProcessData)")
    else:
        raise ValueError(f"未找ToTraceData。可用键: {list(data.keys())}")
    
    # Load其他元Data
    metadata = {}
    for key in data.files:
        if key not in ['cpu_traces', 'traces']:
            metadata[key] = data[key]
    
    logger.info(f"  Trace形状: {traces.shape}")
    logger.info(f"  DataClass型: {traces.dtype}")
    
    # Check并报告Has效样本
    nonzero_counts = np.count_nonzero(traces, axis=1)
    avg_samples = np.mean(nonzero_counts)
    logger.info(f"  平均Has效样本数: {avg_samples:.1f}")
    
    if avg_samples < 2:
        logger.warning("[WARNING] Warning: 平均Has效样本数过少，预Process效果可能Not佳")
    
    return traces, metadata


def save_traces_to_npz(output_file: str, traces: np.ndarray, 
                       metadata: dict, stats: Optional[dict] = None):
    """Save预Processed的TraceTonpzFile"""
    save_data = {
        'traces': traces,
        'cpu_traces': traces  # 兼容原始Format
    }
    
    # Save元Data
    for key, value in metadata.items():
        save_data[key] = value
    
    # Save统计Information
    if stats:
        for key, value in stats.items():
            if isinstance(value, (list, tuple)):
                save_data[f'preprocess_{key}'] = np.array(value)
            elif isinstance(value, (int, float, str, bool)):
                save_data[f'preprocess_{key}'] = value
    
    np.savez_compressed(output_file, **save_data)
    logger.info(f"[OK] 预ProcessData已Save: {output_file}")
    logger.info(f"  Output形状: {traces.shape}")


def parse_window_arg(window_str: str) -> tuple:
    """Parse窗口Parameters '100:500' -> (100, 500)"""
    parts = window_str.split(':')
    if len(parts) != 2:
        raise ValueError(f"窗口FormatError: {window_str}，应For 'start:end'")
    
    start = int(parts[0])
    end = int(parts[1]) if parts[1] else None
    return start, end


def main():
    parser = argparse.ArgumentParser(
        description='PowerTrace预ProcessTool',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__
    )
    
    # InputOutput
    parser.add_argument('input', type=str, help='InputnpzFile')
    parser.add_argument('-o', '--output', type=str, default=None,
                       help='OutputnpzFile (Default: Input file名_preprocessed.npz)')
    
    # 预Process开关
    parser.add_argument('--no-baseline', action='store_true',
                       help='Disable基线校正')
    parser.add_argument('--no-align', action='store_true',
                       help='DisableTraceAlignment')
    parser.add_argument('--no-filter', action='store_true',
                       help='DisableFiltering')
    parser.add_argument('--no-norm', action='store_true',
                       help='DisableNormalization')
    parser.add_argument('--denoise', action='store_true',
                       help='EnableDenoising')
    parser.add_argument('--remove-outliers', action='store_true',
                       help='EnableOutlierRemove')
    
    # AlignmentParameters
    parser.add_argument('--align-method', type=str, default='correlation',
                       choices=['correlation', 'dtw'],
                       help='AlignmentMethod (Default: correlation)')
    parser.add_argument('--align-offset', type=int, default=50,
                       help='最大Alignment偏移量 (Default: 50)')
    
    # FilteringParameters
    parser.add_argument('--filter-type', type=str, default='lowpass',
                       choices=['lowpass', 'bandpass', 'highpass'],
                       help='Filtering器Class型 (Default: lowpass)')
    parser.add_argument('--filter-cutoff', type=float, default=0.1,
                       help='Filtering截止频率 (Default: 0.1)')
    parser.add_argument('--filter-order', type=int, default=4,
                       help='Filtering器阶数 (Default: 4)')
    
    # NormalizationParameters
    parser.add_argument('--norm-method', type=str, default='zscore',
                       choices=['zscore', 'minmax', 'robust'],
                       help='NormalizationMethod (Default: zscore)')
    
    # DenoisingParameters
    parser.add_argument('--denoise-method', type=str, default='moving_average',
                       choices=['moving_average', 'wavelet'],
                       help='DenoisingMethod (Default: moving_average)')
    parser.add_argument('--moving-window', type=int, default=5,
                       help='移动平均窗口大小 (Default: 5)')
    parser.add_argument('--wavelet-threshold', type=float, default=0.1,
                       help='小波Denoising阈值 (Default: 0.1)')
    
    # Outlier检测Parameters
    parser.add_argument('--outlier-method', type=str, default='zscore',
                       choices=['zscore', 'iqr'],
                       help='Outlier检测Method (Default: zscore)')
    parser.add_argument('--outlier-threshold', type=float, default=3.0,
                       help='Outlier阈值 (Default: 3.0)')
    
    # 重Sampling
    parser.add_argument('--resample', type=float, default=None,
                       help='重Sampling因子 (>1上Sampling, <1下Sampling)')
    
    # Advanced功能
    parser.add_argument('--pca', action='store_true',
                       help='EnablePCA降维')
    parser.add_argument('--pca-components', type=int, default=50,
                       help='PCA保留成分数 (Default: 50)')
    
    parser.add_argument('--ica', action='store_true',
                       help='EnableICA信号分离')
    parser.add_argument('--ica-components', type=int, default=50,
                       help='ICA成分数 (Default: 50)')
    
    parser.add_argument('--pooling', type=str, default=None,
                       choices=['average', 'max', 'median'],
                       help='Trace池化Method')
    parser.add_argument('--pooling-size', type=int, default=2,
                       help='池化窗口大小 (Default: 2)')
    
    parser.add_argument('--fft-filter', action='store_true',
                       help='EnableFFT频域Filtering')
    parser.add_argument('--fft-low', type=float, default=0.0,
                       help='FFT低频截止 (Default: 0.0)')
    parser.add_argument('--fft-high', type=float, default=0.5,
                       help='FFT高频截止 (Default: 0.5)')
    
    parser.add_argument('--window', type=str, default=None,
                       help='Extract时间窗口 (Format: start:end, 例: 100:500)')
    
    # 其他Options
    parser.add_argument('--verbose', action='store_true',
                       help='DisplayDetailedInformation')
    parser.add_argument('--show-stats', action='store_true',
                       help='Display预Process统计Information')
    
    args = parser.parse_args()
    
    # If没Has指定Output file，自动Generate
    if args.output is None:
        import os
        base_name = os.path.splitext(args.input)[0]
        args.output = f"{base_name}_preprocessed.npz"
    
    # Settings日志级别
    if args.verbose:
        logging.getLogger().setLevel(logging.DEBUG)
    
    # PrintConfiguration
    logger.info("=" * 70)
    logger.info("  PowerTrace预ProcessTool")
    logger.info("=" * 70)
    logger.info(f"Input file: {args.input}")
    logger.info(f"Output file: {args.output}")
    
    try:
        # 1. LoadData
        logger.info("\n[1/3] LoadTraceData...")
        traces, metadata = load_traces_from_npz(args.input)
        
        # 2. Configuration预Process
        logger.info("\n[2/3] Configuration预ProcessParameters...")
        config = PreprocessingConfig(
            # Basic开关
            enable_baseline_correction=not args.no_baseline,
            enable_alignment=not args.no_align,
            enable_filtering=not args.no_filter,
            enable_normalization=not args.no_norm,
            enable_denoising=args.denoise,
            enable_outlier_removal=args.remove_outliers,
            enable_resampling=(args.resample is not None),
            
            # Alignment
            alignment_method=args.align_method,
            max_alignment_offset=args.align_offset,
            
            # Filtering
            filter_type=args.filter_type,
            filter_cutoff=args.filter_cutoff,
            filter_order=args.filter_order,
            
            # Normalization
            normalization_method=args.norm_method,
            
            # Denoising
            denoising_method=args.denoise_method,
            moving_avg_window=args.moving_window,
            wavelet_threshold=args.wavelet_threshold,
            
            # Outlier
            outlier_method=args.outlier_method,
            outlier_threshold=args.outlier_threshold,
            
            # 重Sampling
            resample_factor=args.resample if args.resample else 1.0,
            
            # PCA/ICA
            enable_pca=args.pca,
            pca_components=args.pca_components,
            enable_ica=args.ica,
            ica_components=args.ica_components,
            
            # 池化
            enable_trace_pooling=(args.pooling is not None),
            pooling_method=args.pooling if args.pooling else 'average',
            pooling_size=args.pooling_size,
            
            # FFTFiltering
            enable_fft_filtering=args.fft_filter,
            fft_low_freq=args.fft_low,
            fft_high_freq=args.fft_high,
            
            # 时间窗口
            enable_window_extraction=(args.window is not None),
        )
        
        # Parse窗口Parameters
        if args.window:
            start, end = parse_window_arg(args.window)
            config.window_start = start
            config.window_end = end
        
        # DisplayEnable的预ProcessStep
        enabled_steps = []
        if config.enable_baseline_correction:
            enabled_steps.append("基线校正")
        if config.enable_alignment:
            enabled_steps.append(f"TraceAlignment({config.alignment_method})")
        if config.enable_filtering:
            enabled_steps.append(f"Filtering({config.filter_type})")
        if config.enable_denoising:
            enabled_steps.append(f"Denoising({config.denoising_method})")
        if config.enable_normalization:
            enabled_steps.append(f"Normalization({config.normalization_method})")
        if config.enable_outlier_removal:
            enabled_steps.append(f"OutlierRemove({config.outlier_method})")
        if config.enable_resampling:
            enabled_steps.append(f"重Sampling(×{config.resample_factor})")
        if config.enable_pca:
            enabled_steps.append(f"PCA降维({config.pca_components})")
        if config.enable_ica:
            enabled_steps.append(f"ICA分离({config.ica_components})")
        if config.enable_trace_pooling:
            enabled_steps.append(f"池化({config.pooling_method})")
        if config.enable_fft_filtering:
            enabled_steps.append("FFTFiltering")
        if config.enable_window_extraction:
            enabled_steps.append(f"窗口Extract[{config.window_start}:{config.window_end}]")
        
        logger.info(f"Enable的预ProcessStep: {len(enabled_steps)}")
        for step in enabled_steps:
            logger.info(f"  • {step}")
        
        # 3. Execute预Process
        logger.info("\n[3/3] Execute预Process...")
        preprocessor = TracePreprocessor(config)
        processed_traces, stats = preprocessor.preprocess(traces)
        
        # Display统计Information
        if args.show_stats:
            logger.info("\n预Process统计Information:")
            logger.info(f"  原始形状: {stats['original_shape']}")
            logger.info(f"  Processed形状: {processed_traces.shape}")
            logger.info(f"  原始SNR: {stats['original_snr']:.2f} dB")
            logger.info(f"  ProcessedSNR: {stats['processed_snr']:.2f} dB")
            logger.info(f"  SNR提升: {stats['snr_improvement']:.2f} dB")
            logger.info(f"  方差降低: {stats['variance_reduction']*100:.1f}%")
            
            if 'outliers_removed' in stats:
                logger.info(f"  RemoveOutlier: {stats['outliers_removed']} 条")
            
            if 'pca_explained_variance' in stats:
                total_var = np.sum(stats['pca_explained_variance'])
                logger.info(f"  PCA解释方差: {total_var*100:.1f}%")
        
        # 4. SaveResult
        logger.info("\nSave预ProcessResult...")
        
        # IfRemove了Outlier，RequireUpdate元Data中的索引
        if 'valid_indices' in stats and 'indices' in metadata:
            valid_idx = stats['valid_indices']
            # UpdateAll与Trace索引相关的元Data
            for key in ['indices', 'start_times', 'end_times', 'durations']:
                if key in metadata:
                    metadata[key] = metadata[key][valid_idx]
        
        save_traces_to_npz(args.output, processed_traces, metadata, stats)
        
        logger.info("\n" + "=" * 70)
        logger.info("  预ProcessCompleted！")
        logger.info("=" * 70)
        logger.info(f"[OK] Input: {traces.shape[0]} 条Trace, {traces.shape[1]} 个Sampling点")
        logger.info(f"[OK] Output: {processed_traces.shape[0]} 条Trace, {processed_traces.shape[1]} 个Sampling点")
        logger.info(f"[OK] SNR提升: {stats['snr_improvement']:.2f} dB")
        
        return 0
        
    except Exception as e:
        logger.error(f"\nError: {e}")
        if args.verbose:
            import traceback
            traceback.print_exc()
        return 1


if __name__ == '__main__':
    sys.exit(main())
